Skip to content

fix(extensions): reject aliases that shadow core commands - #4558

Merged
mnriem merged 2 commits into
github:mainfrom
chelsealong:fix/4555-alias-shadows-core-command
Sep 17, 2026
Merged

mnriem merged 2 commits into
github:mainfrom
chelsealong:fix/4555-alias-shadows-core-command

Conversation

@chelsealong

Copy link
Copy Markdown
Contributor

Summary

Fixes #4555.

_validate_install_conflicts is documented as rejecting installs that "would shadow core or installed extension commands", but _get_installed_command_name_map only walks self.registry — installed extensions. Core command names were never in the map it consults, so an extension could declare a core command's fully-qualified name (e.g. speckit.taskstoissues) as an alias and install successfully, shadowing the core command.

Primary command names are already namespace-checked against CORE_COMMAND_NAMES in _collect_manifest_command_names (a primary name like speckit.taskstoissues is correctly rejected — it fails the required two-segment speckit.{extension}.{command} pattern). Aliases are intentionally free-form (existing community extensions use short aliases like speckit.verify), so they can't be pattern-checked the same way — the only safe fix is to compare declared alias names directly against the fully-qualified core command names in the install-conflict check.

Fix

In _validate_install_conflicts (src/specify_cli/extensions/__init__.py), build core_command_names from CORE_COMMAND_NAMES (qualified as speckit.<name>) and reject any declared command/alias name that matches, in addition to the existing installed-extension check.

Test plan

  • Added test_install_rejects_alias_shadowing_core_command to tests/test_extensions.py, reproducing the exact scenario from the issue: a manifest declaring speckit.probe.taskstoissues with alias speckit.taskstoissues (a live core command).
  • Fail-before / pass-after: reverted just the source change (git checkout HEAD~1 -- src/specify_cli/extensions/__init__.py), confirmed the new test fails (Failed: DID NOT RAISE ValidationError), restored the fix, confirmed it passes.
  • Full tests/test_extensions.py: 536 passed.
  • Full suite (python -m pytest tests -q): 8054 passed, 12 skipped, no failures.

AI Disclosure

  • I did use AI assistance (describe below)

This fix was implemented with Claude Code (Claude Sonnet 5), based on the reproduction and root-cause analysis in the linked issue. I reviewed the code paths involved (_collect_manifest_command_names, _get_installed_command_name_map, _validate_install_conflicts), confirmed the fail-before/pass-after test behavior described above, and ran the full test suite myself before opening this PR.

🤖 Generated with Claude Code

_validate_install_conflicts only compared declared command/alias names
against installed extensions, never against core command names, so an
extension could claim a core command's fully-qualified name (e.g.
'speckit.taskstoissues') as an alias and shadow it silently. Primary
names are already namespace-checked against CORE_COMMAND_NAMES, but
aliases are intentionally free-form, so this can only be caught in the
install-conflict check by comparing against the qualified core names
directly.

Fixes github#4555
@chelsealong
chelsealong requested a review from mnriem as a code owner September 12, 2026 11:24
@mnriem mnriem added the triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review label Sep 12, 2026
@mnriem
mnriem requested a balanced review from Copilot September 15, 2026 16:13
@mnriem mnriem added triage-can-wait Verdict: valid and in-scope but deprioritized; held behind the evidence gate and removed triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review labels Sep 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Equivalent plain or hyphenated aliases can still shadow core commands after agent-specific name transformation.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Prevents extension aliases from shadowing core commands.

Changes:

  • Adds core-name collision validation.
  • Adds a regression test for speckit.taskstoissues.
  • Fail-before evidence was reported but not independently executed.
File summaries
File Description
src/specify_cli/extensions/__init__.py Checks declared names against core commands.
tests/test_extensions.py Tests canonical core-alias rejection.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/specify_cli/extensions/__init__.py Outdated
Comment thread src/specify_cli/extensions/__init__.py Outdated
…adow core commands

Copilot review on github#4558 pointed out that agent-specific output-name
normalization (CommandRegistrar._compute_output_name, and the
Cline/Forge/Junie formatters) collapses speckit.taskstoissues,
taskstoissues, and speckit-taskstoissues to the same on-disk command
name, so the exact-dotted-string check missed the plain and hyphenated
alias spellings. Also fixed the error heading, which said "conflict
with installed extensions" even for a core-only collision.
@chelsealong

Copy link
Copy Markdown
Contributor Author

Addressed both review comments in 4a6ac69:

  • Added _normalize_shadow_name and used it to compare declared alias names against core commands using their on-disk output form (dots → hyphens, speckit- prefix), matching the normalization CommandRegistrar._compute_output_name and the Cline/Forge/Junie formatters apply. This catches taskstoissues and speckit-taskstoissues, not just the exact speckit.taskstoissues spelling. Added a parametrized regression test for both forms, confirmed it fails without the fix (DID NOT RAISE ValidationError) and passes with it.
  • Fixed the error heading to say "conflict with core or installed extension commands" instead of implying only installed extensions can collide.

Full suite: 8056 passed, 12 skipped. ruff check src tests: clean.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused fix matches the reported defect and includes positive existing coverage plus targeted regression tests.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@mnriem
mnriem merged commit dc5b4da into github:main Sep 17, 2026
15 checks passed
@mnriem

mnriem commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage-can-wait Verdict: valid and in-scope but deprioritized; held behind the evidence gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

An extension alias can shadow a core command; _validate_install_conflicts only checks installed extensions

3 participants